/*
 * This file is part of the Chumby Computer Vision Project: www.EMGRobotics.com
 *
 * Copyright (c) 2010 individual Chumby Computer Vision contributors. 
 * See end of header for contributor list.
 *
 * This code is licensed to you under the terms of the Apache License, version
 * 2.0, or, at your option, the terms of the GNU General Public License,
 * version 2.0. See the APACHE20 and GPL2 files for the text of the licenses,
 * or the following URLs:
 * http://www.apache.org/licenses/LICENSE-2.0
 * http://www.gnu.org/licenses/gpl-2.0.txt
 *
 * If you redistribute this file in source form, modified or unmodified, you
 * may:
 *   1) Leave this header intact and distribute it under the same terms,
 *      accompanying it with the APACHE20 and GPL20 files, or
 *   2) Delete the Apache 2.0 clause and accompany it with the GPL2 file, or
 *   3) Delete the GPL v2 clause and accompany it with the APACHE20 file
 *
 * In all cases you must keep this copyright notice intact, along with the
 * list of contributors at the end of this header.
 *
 * Binary distributions must follow the binary distribution requirements of
 * either License.
 *
 * Contributors: 
 *			Eric Gregori ( www.EMGRobotics.com )
 */
//
//**********************************************************************************
//
//  ChumbyCV - Chumby Computer Vision Application framework
//
//  This framework provides a interface to a V4L2 camera(input), and a interface to a
//  Linux framebuffer(output).
//  This very simple framework makes it easy to develop Computer Vision applications
//  for the Chumby internet device.
//  This Framework has been tested on Chumby Ones, using various Logitech USB cameras.
//  See www.EMGRobotics.com for more information.
//
//  Compile this file, with a file containing your algorithm ( see example algorithm files )
//  gcc ChumbyCV.c ChumbyCV_V4L2.c ChumbyCV_MotionDetect.c -o ChumbyCV
//  Replace ChumbyCV_Motion.c  with your algorithm file.
//
//  Don't forget to chmod 777 ChumbyCV after building 
//
//**********************************************************************************
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <fcntl.h>

//**********************************************************************************
// Constants
//**********************************************************************************
#define EMG_WIDTH	320
#define EMG_HEIGHT	240
#define SETPIXEL(r,g,b)	

//**********************************************************************************
// External function declared in Algorithm file
//**********************************************************************************
extern uint32_t ChumbyCV_Algorithm( unsigned char* CameraData );
extern int ChumbyCV_Parameters( int argc, char *argv[], int width, int height );
extern void ChumbyCV_ShutDown( void ); 

//**********************************************************************************
// External function declared in V4L2 file
//**********************************************************************************
extern void start_capturing( char* dev_name, int width, int height );
extern void stop_capturing( void );
extern void uninit_device( void );
extern void close_device( void );
extern unsigned char *GetFrame( void );
extern void ReleaseFrame( void );

//**********************************************************************************
// Global
//**********************************************************************************
int 			fbfd 		= 0;
struct 			fb_var_screeninfo vinfo;
struct 			fb_fix_screeninfo finfo;
long int 		screensize 	= 0;
short 			*fbp 		= 0;


//************************************************************************************
//*************************************************************************************
//****************************                       **********************************
//****************************     framebuffer       **********************************
//****************************                       **********************************
//*************************************************************************************
//*************************************************************************************
void OpenChumbyFrameBuffer( void )
{
	int			x,y;
	unsigned int		location;

    // Open the file for reading and writing
    fbfd = open("/dev/fb0", O_RDWR);
    if (!fbfd) {
        printf("Error: cannot open framebuffer device.\n");
        exit(1);
    }
    printf("The framebuffer device was opened successfully.\n");

    // Get fixed screen information
    if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo)) {
        printf("Error reading fixed information.\n");
        exit(2);
    }

    // Get variable screen information
    if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo)) {
        printf("Error reading variable information.\n");
        exit(3);
    }

    printf("%dx%d, %dbpp\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel );

    // Figure out the size of the screen in bytes
    screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;

    // Map the device to memory
    fbp = (short *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED,
                       fbfd, 0);
    if ((int)fbp == -1) {
        printf("Error: failed to map framebuffer device to memory.\n");
        exit(4);
    }
    printf("The framebuffer device was mapped to memory successfully.\n");

    printf( "\nvinfo.xres = %d", vinfo.xres );
    printf( "\nvinfo.yres = %d", vinfo.yres );
    printf( "\nvinfo.bits_per_pixel = %d", vinfo.bits_per_pixel );
    printf( "\nvinfo.xoffset = %d", vinfo.xoffset );
    printf( "\nvinfo.yoffset = %d", vinfo.yoffset );
    printf( "\nfinfo.line_length = %d\n\n", finfo.line_length );
}


//*************************************************************************************
//*************************************************************************************
//*******************************                     *********************************
//****************************    Algorithm CallBack      *****************************
//*******************************                     *********************************
//*************************************************************************************
//*************************************************************************************
inline void SetPixel( uint32_t index, unsigned char r, unsigned char g, unsigned char b )
{
	*((unsigned short *)(fbp + index)) = (0x0800*(r>>3))|(0x0020*(g>>2))|(0x0001*(b>>3));
}


//*************************************************************************************
//*************************************************************************************
//*****************************                 ***************************************
//*****************************      MAIN       ***************************************
//*****************************                 ***************************************
//*************************************************************************************
//*************************************************************************************
int main (int argc, char *argv[])
{
	uint32_t 	i;
	unsigned char	*data;
	char my_device[64];

	fprintf( stderr, "\nChumbyCV Algorithm Test Framework\n" );
	if( argc < 2 )
	{
		fprintf( stderr, "\nUsage: %s V4L2_device [parameters]", argv[0] );
		fprintf( stderr, "\nExample: %s /dev/video0\n\n\n", argv[0] );
		exit(1);
	}

	strcpy(my_device, argv[1] );

	// Send command line options to Algorithm
	if( ChumbyCV_Parameters( argc, argv, EMG_WIDTH, EMG_HEIGHT ) )
		exit(1);

    	OpenChumbyFrameBuffer();
        start_capturing( my_device, EMG_WIDTH, EMG_HEIGHT );
	printf( "\n\n" );

    	for (;;)
    	{
		data = GetFrame();
		if( data == NULL ) continue;
		// YUYV
		// y1 = data[CaptureIndex++];
		// u  = data[CaptureIndex++];
		// y2 = data[CaptureIndex++];
		// v  = data[CaptureIndex++];
		(void)ChumbyCV_Algorithm( data );
#if 0			
	        for(i=0, index=0; i<((EMG_WIDTH*EMG_HEIGHT)); i++ )
	        {
		   	// RGB24 output display - 0x00rrbbgg
		   	temp = data[index+=2];
		   	emg_data[i] = SETPIXEL(temp,temp,temp);
	        }
#endif
		ReleaseFrame();	
    	} // for(;;)

	stop_capturing ();
    	munmap(fbp, screensize);
    	close(fbfd);
  	return 0;
}


